#include <fstream>
#include <string>
#include <atomic>
#include <map>

#pragma once

namespace kawauso {

    /**
     * LocalStore is a key-value storage system based on file. It persists newly added key-value pairs to a file in
     * sequential order. If there's a need to delete or overwrite existing key-value pairs, deletion markers or new
     * key-values are also written to the file sequentially. <br/>
     * To create a new instance, you need to provide a relative file path to open the corresponding file (creating the
     * file or folder if it doesn't exist). The first thing is to read the records from the file, if any, and then replay
     * them to restore key-value pairs into the in-memory data structure. It's essential to promptly clean up any records
     * with delete markers or overwritten key-value pairs. To achieve this, existing key-value pairs in memory can be
     * serialized directly to the file, overwriting the original records.
     */
    class LocalStore {
        std::map<std::string, std::string> pairs;
        FILE * fileStream;
    public:
        explicit LocalStore(const std::string& filename);
        ~LocalStore();

        /**
         * Get reference of value associated with the key.
         * @tparam V value type
         * @param key key
         * @return const reference of value
         */
        template<typename V>
        const V& get(const std::string &key);

        /**
         * Set key-value pair based on the given key and value.
         * @tparam V value type
         * @param key key
         * @param value value
         */
        template<typename V>
        void set(const std::string &key, const V &value);

        /**
         * Query whether the key exists.
         * @param key key
         * @return returns true if key exists, otherwise returns false
         */
        bool exists(const std::string &key);

        /**
         * Remove key-value pair if the key exists.
         * @param key key
         */
        void remove(const std::string &key);

    private:
        bool readBufferFromFile(std::string &buffer, int length);
        bool loadRecordFromFile();
        void writeToFile(const std::pair<const std::string, std::string> &pair);
    public:
        static LocalStore global;
    };

    template<typename V>
    const V& LocalStore::get(const std::string &key) {
        char *ptr = pairs[key].data();
        return *(V *)ptr;
    }

    template<>
    const std::string& LocalStore::get<std::string>(const std::string &key);

    template<typename V>
    void LocalStore::set(const std::string &key, const V &value) {
        set(key, std::string((char *)&value, sizeof(V)));
    }

    template<>
    void LocalStore::set<std::string>(const std::string &key, const std::string &value);

}